; ==============================================================================
; Apple II [$D0 ROM] (341-0016) - Programmer's Aid #1 [1978]
; ------------------------------------------------------------------------------
; Part 6 [$D717~$D7FF]: Music Subroutine by Gary J. Shannon;
; Copyright (c) 1978 by Apple Computer Inc. All Rights Reserved
; ------------------------------------------------------------------------------
; Instructions are in the Programmer's Aid #1 Installation and Operating Manual
; ==============================================================================
; Analyzed (via McFadden's SourceGen) by James Davis [Last Updated: 2020-07-10]
; ==============================================================================
;
; ==============================================================================
; Music Routine 6502 Equates: Zero Page Work Areas; & Parameter Passing Areas
; ==============================================================================
;
DOWNTIME EQU $00 {addr/1} ;Speaker Negative Pulse Width
UPTIME EQU $01 {addr/1} ;Speaker Positive Pulse Width
DURATION EQU $02 {addr/2} ;Musical Note Time-Duration Counter
TIMBRE EQU $02FD {addr/1} ;Musical Note Timbre Value (Poke 765)
TIME EQU $02FE {addr/1} ;Musical Note Time Value (Poke 766)
PITCH EQU $02FF {addr/1} ;Musical Note Pitch Value (Poke 767)
SPEAKER EQU $C030 {addr/1} ;Speaker Data Output Toggle Switch
ORG $D717
;
; ==============================================================================
; Music Subroutine by Gary J. Shannon [Do Pokes, then Call -10473 from BASIC]
; ==============================================================================
; DURATION EQU $02 ;Renamed to match Instructions; Original Name: LENGTH
; TIMBRE EQU $02FD ;Renamed to match Instructions; Original Name: VOICE
; TIME EQU $02FE ;Renamed to match Instructions; Original Name: LONG
; PITCH EQU $02FF ;Renamed to match Instructions; Original Name: NOTE
; ------------------------------------------------------------------------------
;
D717: 4C 4E D7 ENTRY JMP LOOKUP ;Get Pulse Widths (Duty Cycle Data)
; ------------------------------------------------------------------------------
; Play One Note: Musical Note Cycles are divided into UPTIME & DOWNTIME halves;
; Musical Note Duty Cycle Data is from NOTES: UPTIME & DOWNTIME;
; Musical Note Time-Duration Count is kept in DURATION
; ------------------------------------------------------------------------------
;
; ----------------------------------- ;UPTIME Half-Cycle:
D71A: A4 01 PLAY1 LDY UPTIME ;Get Positive Pulse Width
D71C: AD 30 C0 LDA SPEAKER ;Toggle Speaker Data Output
D71F: E6 02 PLAY2 INC DURATION ;Advance Note Time-Duration Counter, Low
D721: D0 05 BNE PATH1 ;Branch if Duration (Low) is Not Expired
D723: E6 03 INC DURATION+1 ;Advance Note Time-Duration Counter, High
D725: D0 05 BNE PATH2 ;Branch if Duration (High) is Not Expired
D727: 60 RTS ;Return to Caller; Time-Duration Expired
; ;Do Time Adjustments:
D728: EA PATH1 NOP ;Delay (2 Machine Cycles)
D729: 4C 2C D7 JMP PATH2 ;Delay (3 Machine Cycles) More
D72C: 88 PATH2 DEY ;Reduce Pulse Counter (Width)
D72D: F0 05 BEQ PLAY0 ;Toggle if Pulse Count (Width) is Expired
D72F: 4C 32 D7 JMP PATH3 ;Continue UPTIME if Count is Not Expired
; ;Jump Delays (3 Machine Cycles) More
D732: D0 EB PATH3 BNE PLAY2 ;UPTIME: Same # of Cyles; Always Taken
; ----------------------------------- ;DOWNTIME Half-Cycle:
D734: A4 00 PLAY0 LDY DOWNTIME ;Get Negative Pulse Width
D736: AD 30 C0 LDA SPEAKER ;Toggle Speaker Data Output
D739: E6 02 PLAY3 INC DURATION ;Advance Note Time-Duration Counter, Low
D73B: D0 05 BNE PATH4 ;Branch if Duration (Low) is Not Expired
D73D: E6 03 INC DURATION+1 ;Advance Note Time-Duration Counter, High
D73F: D0 05 BNE PATH5 ;Branch if Duration (High) is Not Expired
D741: 60 RTS ;Return to Caller; Time-Duration Expired
; ;Do Time Adjustments:
D742: EA PATH4 NOP ;Delay (2 Machine Cycles)
D743: 4C 46 D7 JMP PATH5 ;Delay (3 Machine Cycles) More
D746: 88 PATH5 DEY ;Reduce Pulse Counter (Width)
D747: F0 D1 BEQ PLAY1 ;Toggle if Pulse Count (Width) is Expired
D749: 4C 4C D7 JMP PATH6 ;Continue DOWNTIME if Count is Not Expired
; ;Jump Delays (3 Machine Cycles) More
D74C: D0 EB PATH6 BNE PLAY3 ;DOWNTIME: Same # of Cyles; Always Taken
; ------------------------------------------------------------------------------
; Note Table Lookup Subroutine: Gets a Note's UPTIME & DOWNTIME from its PITCH &
; Sets its DURATION in accordance with its TIMBRE
; ------------------------------------------------------------------------------
; DURATION EQU $02 ;Musical Note Time-Duration Counter
; TIMBRE EQU $02FD ;Musical Note Timbre Value (Poke 765)
; TIME EQU $02FE ;Musical Note Time Value (Poke 766)
; PITCH EQU $02FF ;Musical Note Pitch Value (Poke 767)
; ------------------------------------------------------------------------------
D74E: AD FF 02 LOOKUP LDA PITCH ;Get Musical Note Pitch Value (User Poked)
D751: 0A ASL A ;Double Musical Note Pitch Value (1 of 2)
D752: A8 TAY ;Set Indexed Addressing Pointer
D753: B9 96 D7 LDA NOTES,Y ;Get Note UPTIME (Positive Pulse Width)=
D756: 85 00 STA DOWNTIME ;Set Note DOWNTIME (Negative Pulse Width)=
; ----------------------------------- ;Shift Time According to Note's TIMBRE:
D758: AD FD 02 LDA TIMBRE ;Get Note's Timbre Value (User Poked)
D75B: 4A SHIFT LSR A ;Halve Note Timbre Value (Duty Cycle)
D75C: F0 04 BEQ DONE ;Exit if Halving Results in a Zero Value
D75E: 46 00 LSR DOWNTIME ;Else, Halve Negative Pulse Width, too
D760: D0 F9 BNE SHIFT ;Loop if Halving Result is Not Zero yet
; ----------------------------------- ;Compute New UPTIME/DOWNTIME Pulse Widths:
D762: B9 96 D7 DONE LDA NOTES,Y ;Get Original Note's Positive Pulse Width
; ;Compute Difference:
D765: 38 SEC ;Prep to Subtract w/o Borrow [A-Data-!C]
D766: E5 00 SBC DOWNTIME ;Subtract Shifted Negative Pulse Width
D768: 85 01 STA UPTIME ;*** Set New Positive Pulse Width ***
D76A: C8 INY ;Advance Indexed Addressing Pointer
D76B: B9 96 D7 LDA NOTES,Y ;Get Original Note's Negative Pulse Width
; ;Add Difference [Add w/ Carry: A+Data+C]:
D76E: 65 00 ADC DOWNTIME ;Add Shifted Negative Pulse Width
D770: 85 00 STA DOWNTIME ;*** Set New Negative Pulse Width ***
; ----------------------------------- ;Compute Compliment of Duration Count:
D772: A9 00 LDA #0 ;Prepare to Subtract from Zero
D774: 38 SEC ;Prep to Subtract w/o Borrow [A-Data-!C]
D775: ED FE 02 SBC TIME ;Subtract Musical Note Time Value
D778: 85 03 STA DURATION+1 ;Set Note Time-Duration Counter, High
D77A: A9 00 LDA #0 ;Clear Accumulator
D77C: 85 02 STA DURATION ;Set Note Time-Duration Counter, Low
; ----------------------------------- ;Prepare to Play Musical Note:
D77E: A5 01 LDA UPTIME ;Get Positive Pulse Width
D780: D0 98 BNE PLAY1 ;If Note is Not a Rest Note, Play it ...
;
; ------------------------------------------------------------------------------
; Rest Note Subroutine: Plays Note #0 Silently, with same/regular Note Durations
; ------------------------------------------------------------------------------
;
; Rest Note Iterations: Do UPTIME 1st, DOWNTIME 2nd; == Same # of UP/DOWN Cyles
;
; ;Do Time Adjustments:
D782: EA REST NOP ;Delay (2 Machine Cycles)
D783: EA NOP ;Delay (2 Machine Cycles) More
D784: 4C 87 D7 JMP REST2 ;Delay (3 Machine Cycles) More
D787: E6 02 REST2 INC DURATION ;Advance Note Time-Duration Counter, Low
D789: D0 05 BNE REST3 ;Branch if Duration (Low) is Not Expired
D78B: E6 03 INC DURATION+1 ;Advance Note Time-Duration Counter, High
D78D: D0 05 BNE REST4 ;Branch if Duration (High) is Not Expired
D78F: 60 RTS ;Return to Caller; Time-Duration Expired
D790: EA REST3 NOP ;Delay (2 Machine Cycles)
D791: 4C 94 D7 JMP REST4 ;Delay (3 Machine Cycles) More
D794: D0 EC REST4 BNE REST ;Do DOWNTIME: Same Cyle; Always Taken
; ------------------------------------------------------------------------------
; Notes Table: Values are in UP/DOWN Pairs; e.g., 1st Note is a Rest [#0 =(0,0)]
; ------------------------------------------------------------------------------
;
D796: 00 00 NOTES HEX 0000 ;Note #00: A Rest (A Silence, Not a Pitch)
; Chromatic Octave: "Contra" [Below Bass Clef] - [Low End of Partial Octave]
D798: F6 F6 HEX F6F6 ;Note #01: Contra F or (uppercase) FF
D79A: E8 E8 HEX E8E8 ;Note #02: Contra F# (F-sharp or G-flat)
D79C: DB DB HEX DBDB ;Note #03: Contra G or (uppercase) GG
D79E: CF CF HEX CFCF ;Note #04: Contra G# (G-sharp or A-flat)
D7A0: C3 C3 HEX C3C3 ;Note #05: Contra A or (uppercase) AA
D7A2: B8 B8 HEX B8B8 ;Note #06: Contra A# (A-sharp or B-flat)
D7A4: AE AE HEX AEAE ;Note #07: Contra B or (uppercase) BB
; Chromatic Octave: "Great" [Bottom of & below Bass Clef]
D7A6: A4 A4 HEX A4A4 ;Note #08: Great C or (uppercase) C
D7A8: 9B 9B HEX 9B9B ;Note #09: Great C# (C-sharp or D-flat)
D7AA: 92 92 HEX 9292 ;Note #10: Great D or (uppercase) D
D7AC: 8A 8A HEX 8A8A ;Note #11: Great D# (D-sharp or E-flat)
D7AE: 82 82 HEX 8282 ;Note #12: Great E or (uppercase) E
D7B0: 7B 7B HEX 7B7B ;Note #13: Great F or (uppercase) F
D7B2: 74 74 HEX 7474 ;Note #14: Great F# (F-sharp or G-flat)
D7B4: 6D 6E HEX 6D6E ;Note #15: Great G or (uppercase) G
D7B6: 67 68 HEX 6768 ;Note #16: Great G# (G-sharp or A-flat)
D7B8: 61 62 HEX 6162 ;Note #17: Great A or (uppercase) A
D7BA: 5C 5C HEX 5C5C ;Note #18: Great A# (A-sharp or B-flat)
D7BC: 57 57 HEX 5757 ;Note #19: Great B or (uppercase) B
; Chromatic Octave: "Small" [Top of Bass Clef] (letters may be lowercase)
D7BE: 52 52 HEX 5252 ;Note #20: Small C or (lowercase) c
D7C0: 4D 4E HEX 4D4E ;Note #21: Small C# (C-sharp or D-flat)
D7C2: 49 49 HEX 4949 ;Note #22: Small D or (lowercase) d
D7C4: 45 45 HEX 4545 ;Note #23: Small D# (D-sharp or E-flat)
D7C6: 41 41 HEX 4141 ;Note #24: Small E or (lowercase) e
D7C8: 3D 3E HEX 3D3E ;Note #25: Small F or (lowercase) f
D7CA: 3A 3A HEX 3A3A ;Note #26: Small F# (F-sharp or G-flat)
D7CC: 36 37 HEX 3637 ;Note #27: Small G or (lowercase) g
D7CE: 33 34 HEX 3334 ;Note #28: Small G# (G-sharp or A-flat)
D7D0: 30 31 HEX 3031 ;Note #29: Small A or (lowercase) a
D7D2: 2E 2E HEX 2E2E ;Note #30: Small A# (A-sharp or B-flat)
D7D4: 2B 2C HEX 2B2C ;Note #31: Small B or (lowercase) b
; Chromatic Octave: "One-Line" [Most of Treble Clef] (letters may be lowercase)
D7D6: 29 29 HEX 2929 ;Note #32: One-line C, C-one, or C^1
; ;^[Middle C is half-way between the Clefs]
D7D8: 26 27 HEX 2627 ;Note #33: One-line C# (C-sharp or D-flat)
D7DA: 24 25 HEX 2425 ;Note #34: One-line D, D-one, or D^1
D7DC: 22 23 HEX 2223 ;Note #35: One-line D# (D-sharp or E-flat)
D7DE: 20 21 HEX 2021 ;Note #36: One-line E, E-one, or E^1
D7E0: 1E 1F HEX 1E1F ;Note #37: One-line F, F-one, or F^1
D7E2: 1D 1D HEX 1D1D ;Note #38: One-line F# (F-sharp or G-flat)
D7E4: 1B 1C HEX 1B1C ;Note #39: One-line G, G-one, or G^1
D7E6: 1A 1A HEX 1A1A ;Note #40: One-line G# (G-sharp or A-flat)
D7E8: 18 19 HEX 1819 ;Note #41: One-line A, A-one, or A^1
D7EA: 17 17 HEX 1717 ;Note #42: One-line A# (A-sharp or B-flat)
D7EC: 15 16 HEX 1516 ;Note #43: One-line B, B-one, or B^1
; Chromatic Octave: "Two-Line" [Top of & above Treble Clef] (may be lowercase)
D7EE: 14 15 HEX 1415 ;Note #43: Two-line C, C-two, or C^2
D7F0: 13 14 HEX 1314 ;Note #33: Two-line C# (C-sharp or D-flat)
D7F2: 12 12 HEX 1212 ;Note #46: Two-line D, D-two, or D^2
D7F4: 11 11 HEX 1111 ;Note #47: Two-line D# (D-sharp or E-flat)
D7F6: 10 10 HEX 1010 ;Note #48: Two-line E, E-two, or E^2
D7F8: 0F 10 HEX 0F10 ;Note #49: Two-line F, F-two, or F^2
D7FA: 0E 0F HEX 0E0F ;Note #50: Two-line F# (F-sharp or G-flat)
; ----------------------------------- ;^[High End of Partial Octave]
D7FC: FF FF HEX FFFF ;Junk Bytes
D7FE: FF FF HEX FFFF ;Junk Bytes